import type {
  SwaggerItem,
  SwaggerSchema,
  SwaggerParameter,
  MethodRequest
} from './types';
import * as formatter from './formatter';

const schemaNameList = new Set<string>();

export const getResType = (key: string, item: SwaggerItem): string => {
  switch (true) {
    case !!item.$ref: {
      const schemaName = item.$ref.replace('#/components/schemas/', '');
      const formatted = formatter.typeName(schemaName);
      schemaNameList.add(formatted);
      return formatted;
    }
    case item.type === 'object' && !!item.additionalProperties: {
      if (item.additionalProperties) {
        const childType = getResType(key, item.additionalProperties);
        return `Map<string, ${childType}>`;
      } else {
        console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
        console.log(JSON.stringify(item));
        return 'Map<string, unknown>';
      }
    }
    case item.type === 'object': {
      const lines: string[] = [];
      if (!item.properties || Object.keys(item.properties).length === 0) {
        return 'Record<string, unknown>';
      }
      lines.push('{');
      const properties = item.properties;
      for (const key in properties) {
        const item = properties[key];
        if (item.description) {
          const description = formatter.description(
            item.description,
            item.format
          );
          lines.push(description);
        }
        const type = getResType(key, item);
        const keyName = formatter.keyName(key);
        lines.push(`${keyName}:${type},`);
      }
      lines.push('}');
      return lines.join('\n');
    }
    case item.type === 'array': {
      if (item.items) {
        const childType = getResType(key, item.items);
        return `${childType}[]`;
      } else if (item.enum) {
        let str = '(';
        str += item.enum.map((item) => `'${item}'`).join(' | ');
        str += ')[]';
        return str;
      } else {
        console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
        console.log(JSON.stringify(item));
        return 'unknown[]';
      }
    }
    case !!item.enum: {
      return item.enum.map((item) => `'${item}'`).join(' | ');
    }
  }
  if (!item.type) {
    console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
    console.log(JSON.stringify(item));
    return 'unknown';
  }
  const types = Array.isArray(item.type) ? item.type : [item.type];
  const results: string[] = [];
  for (const type of types) {
    switch (true) {
      case type === 'binary':
        results.push('File');
        break;
      case type === 'number':
        if (item.format === 'bigdecimal') {
          results.push('bigdecimal');
        } else if (item.format === 'double') {
          results.push('double');
        } else {
          results.push('number');
        }
        break;
      case type === 'integer':
        if (item.format === 'int64') {
          results.push('long');
        } else {
          results.push('number');
        }
        break;
      case type === 'enum':
        results.push('number');
        break;
      case type === 'string':
        if (item.format === 'binary') {
          results.push('File');
          break;
        } else {
          results.push('string');
          break;
        }
      case type === 'boolean':
        results.push('boolean');
        break;
      case type === 'null':
        results.push('null');
        break;
    }
  }
  if (results.length) {
    return results.join(' | ');
  } else {
    console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
    console.log(JSON.stringify(item));
    return 'unknown';
  }
};

export const getResProperty = (
  schemasList: SwaggerSchema[],
  key: string,
  item: SwaggerItem
) => {
  let str = item.description
    ? `${formatter.description(item.description, item.format)}\n`
    : '';
  const typeStr = getResType(key, item);
  if (!str) {
    const schemas = schemasList.find((item) => item.title === typeStr);
    if (schemas?.description) {
      const description = formatter.description(schemas.description);
      if (description) str += `${description}\n`;
    }
  }
  const keyName = formatter.keyName(key);
  // str += `${keyName}${required ? '' : '?'}: ${typeStr}`;
  str += `${keyName}: ${typeStr}`;
  return str;
};
/**
 * implements Partial<Prisma.UserCreateInput> 使用了这个就会包含进“allOf”中，需要注意
 */
export const getResponse = (url: string, api: MethodRequest) => {
  const responseSchema =
    api.responses?.['200']?.content?.['application/json']?.schema;
  const responseSchemaRef = responseSchema?.$ref?.replace(
    '#/components/schemas/',
    ''
  );
  // nestjs/swagger输出的的数组的对象结构不一样，需要显性处理
  // const anySchema = api.responses?.['200']?.content?.['*/*'];
  // let anySchemaRef = anySchema?.schema?.$ref;
  // anySchemaRef = anySchemaRef?.replace('#/components/schemas/', '');
  // const ref = responseSchemaRef ?? anySchemaRef;

  if (responseSchemaRef) {
    const { schema, path } = checkGeneric(responseSchemaRef ?? 'unknown');
    let response = schema;
    for (let i = path.length - 1; i >= 0; i--) {
      response = `${path[i]}<${response}>`;
    }
    return response;
  }

  // const schema = responseSchema ?? anySchema;
  // console.log('[ anySchema ] >', anySchema);
  // console.log('[ responseSchema ] >', responseSchema);
  // if (!schema) {
  console.log(`\x1b[33m没有返回值: ${api.summary}\x1b[0m`);
  console.log(url);
  // }
  return getResType(url, responseSchema ?? {});
};

const checkGeneric = (schema: string) => {
  let str = schema;
  const path: string[] = [];
  while (true) {
    const match = str.match(
      /^(ResponseDataList|ResponseData|ResponseList|RespData|ListData)(.+)$/
    );
    if (!match) break;
    str = match[2];
    path.push(match[1]);
  }
  if (/^Void\d*$/.test(str)) str = 'never';
  return { schema: formatter.typeName(str), path };
};

const parseGeneric = new Map([
  [
    'BasePageRequest',
    (str: string) => `{
    orderField?: string
    pageIndex: string
    pageSize: string
    requestParams: ${str}
    sortType?: boolean
  }`
  ]
]);

const getReqRef = (
  schemasList: SwaggerSchema[],
  ref: string,
  min = 0,
  usedList: SwaggerSchema[] = []
) => {
  const { schema, path } = checkGeneric(ref);
  let str = '';
  if (parseGeneric.has(schema)) {
    const pathParse = parseGeneric.get(schema);
    str = pathParse?.('unknown') ?? 'unknown';
  } else if (schemaNameList.has(schema) && min === 0) {
    str = schema;
  } else {
    const schemaItem = schemasList.find((item) => item.title === schema);
    if (!schemaItem) {
      console.log(`未找到${schema}的schema`);
      return 'unknown';
    } else if (usedList.includes(schemaItem)) {
      str = schema;
    } else {
      const newList = [...usedList, schemaItem];
      if (schemaItem.properties) {
        const lines: string[] = [];
        lines.push('{');
        for (const key in schemaItem.properties) {
          const item = schemaItem.properties[key];
          const required = schemaItem.required?.includes(key) ?? false;
          const type = getReqProperty(
            schemasList,
            key,
            item,
            required,
            min,
            newList
          );
          lines.push(`${type},`);
        }
        lines.push('}');
        str = lines.join('\n');
      } else {
        str = 'unknown';
      }
    }
  }
  if (path.length > 0) {
    for (let i = path.length - 1; i >= 0; i--) {
      const pathParse = parseGeneric.get(path[i]);
      if (!pathParse) {
        console.log(`未找到${path[i]}的解析方法`);
        return 'unknown';
      }
      str = pathParse(str);
    }
  }
  return str;
};

export const getReqType = (
  schemasList: SwaggerSchema[],
  key: string,
  item: SwaggerItem,
  min = 0,
  usedList: SwaggerSchema[] = []
): string => {
  switch (true) {
    case !!item.$ref: {
      const schemaName = item.$ref.replace('#/components/schemas/', '');
      return getReqRef(schemasList, schemaName, min, usedList);
    }
    case item.type === 'object' && !!item.additionalProperties: {
      if (item.additionalProperties) {
        const childType = getReqType(
          schemasList,
          key,
          item.additionalProperties,
          min,
          usedList
        );
        return `Map<string, ${childType}>`;
      } else {
        console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
        console.log(JSON.stringify(item));
        return 'Map<string, unknown>';
      }
    }
    case item.type === 'object': {
      const lines: string[] = [];
      if (!item.properties || Object.keys(item.properties).length === 0) {
        return 'Record<string, unknown>';
      }
      lines.push('{');
      const properties = item.properties;
      for (const key in properties) {
        const item = properties[key];
        if (item.description) {
          const description = formatter.description(
            item.description,
            item.format
          );
          lines.push(description);
        }
        const type = getResType(key, item);
        const keyName = formatter.keyName(key);
        lines.push(`${keyName}:${type},`);
      }
      lines.push('}');
      return lines.join('\n');
    }
    case item.type === 'array': {
      if (item.items) {
        const childType = getReqType(
          schemasList,
          key,
          item.items,
          min,
          usedList
        );
        return `${childType}[]`;
        // } else if (item.enum) {
        //   let str = '(';
        //   str += item.enum.map((item) => `'${item}'`).join(' | ');
        //   str += ')[]';
        //   return str;
      } else {
        console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
        console.log(JSON.stringify(item));
        return 'unknown[]';
      }
    }
    case !!item.enum: {
      return item.enum.map((item) => `'${item}'`).join(' | ');
    }
  }
  if (!item.type) {
    console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
    console.log(JSON.stringify(item));
    return 'unknown';
  }
  const types = Array.isArray(item.type) ? item.type : [item.type];
  const results: string[] = [];
  for (const type of types) {
    switch (true) {
      case type === 'binary':
        results.push('File');
        break;
      case type === 'number':
        if (item.format === 'bigdecimal') {
          results.push('bigdecimal');
        } else if (item.format === 'double') {
          results.push('double');
        } else {
          results.push('number');
        }
        break;
      case type === 'integer':
        if (item.format === 'int64') {
          results.push('long');
        } else {
          results.push('number');
        }
        break;
      case type === 'enum':
        results.push('number');
        break;
      case type === 'string':
        if (item.format === 'binary') {
          results.push('File');
          break;
        } else {
          results.push('string');
          break;
        }
      case type === 'boolean':
        results.push('boolean');
        break;
      case type === 'null':
        results.push('null');
        break;
    }
  }
  if (results.length) {
    return results.join(' | ');
  } else {
    console.log(`\x1b[33m未知的类型: ${key}\x1b[0m`);
    console.log(JSON.stringify(item));
    return 'unknown';
  }
};

export const getReqProperty = (
  schemasList: SwaggerSchema[],
  key: string,
  item: SwaggerItem,
  required: boolean,
  min = 0,
  usedList: SwaggerSchema[] = []
) => {
  let str = item.description
    ? `${formatter.description(item.description, item.format)}\n`
    : '';
  const typeStr = getReqType(schemasList, key, item, min, usedList);
  const keyName = formatter.keyName(key);
  str += `${keyName}${required ? '' : '?'}: ${typeStr}`;
  return str;
};

export const getReqQuery = (
  schemasList: SwaggerSchema[],
  parameters: SwaggerParameter[]
) => {
  const lines: string[] = [];
  for (let i = 0; i < parameters.length; i++) {
    const parameter = parameters[i];
    let str = parameter.description
      ? `${formatter.description(parameter.description)}\n`
      : '';
    if (parameter.schema) {
      const typeStr = getReqType(
        schemasList,
        parameter.name,
        parameter.schema,
        1
      );
      str += `${parameter.name}${parameter.required ? '' : '?'}: ${typeStr}`;
      lines.push(str);
    } else {
      console.log(`\x1b[33m未知的参数类型: ${parameter.name}\x1b[0m`);
      console.log(JSON.stringify(parameter));
      lines.push(`${parameter.name}${parameter.required ? '' : '?'}: unknown`);
    }
  }
  return `{\n${lines.join(',\n')}\n}`;
};

export const getFormDataParameters = (formData: SwaggerSchema) => {
  const required = formData.required ?? [];
  const properties = formData.properties;
  const lines: string[] = [];
  for (const key in properties) {
    const item = properties[key];
    const str = getReqProperty([], key, item, required.includes(key));
    lines.push(str);
  }
  // return `data: {\n${lines.join(',\n')}\n}`;
  return `{\n${lines.join(',\n')}\n}`;
};

export const getReqPath = (path: SwaggerParameter[]) => {
  const lines: string[] = [];
  for (let i = 0; i < path.length; i++) {
    const parameter = path[i];
    let str = parameter.description
      ? `${formatter.description(parameter.description)}\n`
      : '';
    if (parameter.schema) {
      const typeStr = getReqType([], parameter.name, parameter.schema);
      const name = formatter.pathName(parameter.name);
      str += `${name}${parameter.required ? '' : '?'}: ${typeStr}`;
      lines.push(str);
    } else {
      console.log(`\x1b[33m未知的参数类型: ${parameter.name}\x1b[0m`);
      console.log(JSON.stringify(parameter));
      lines.push(`${parameter.name}${parameter.required ? '' : '?'}: unknown`);
    }
  }
  return `${lines.join(',\n')},`;
};

export const getReqUrl = (
  url: string,
  path: SwaggerParameter[] | undefined
) => {
  if (!path) return `'${url}'`;
  for (const item of path) {
    const name = formatter.pathName(item.name);
    url = url.replace(`{${item.name}}`, `\${${name}}`);
  }
  return `\`${url}\``;
};
